package com.github.cdisk.xtrans.util;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.github.cdisk.xtrans.core.BaseComponent;

/**
 * 扫描包下路径 包括本地文件和jar包文件
 * 
 * @author Cdisk
 * @param <T>
 *
 */
public class PackageScanUtil<T> {

	/** 过滤条件-包 */
	private String basePackage = null;

	/** 过滤条件-父类类型 */
	private Class<?> superType = null;

	/** 过滤条件-注解类型 */
//	private Class<?> annotationType = null;

	/** 过滤条件-true:and条件，false:or条件 */
//	private boolean allTrue = false;

	/** 返回结果集 */
	private List<Class<T>> foundList = new ArrayList<Class<T>>();

	/** 类加载器 */
	private ClassLoader classLoader = PackageScanUtil.class.getClassLoader();

	public static void main(String[] args) {
//		List<Class<?>> result = PackageScanUtil.scan("com.github.cdisk.xtrans.component", null, Component.class);
		List<Class<BaseComponent>> result = PackageScanUtil.scan("com.github.cdisk.xtrans.component", BaseComponent.class);
		for (Class<?> class1 : result) {
			System.out.println(class1);
		}
	}

	/**
	 * 扫描package包下的class文件，父类条件和注解条件必须都满足
	 * 
	 * @param basePackage    基础包位置
	 * @param superType      父类类型
	 * @param annotationType 注解类型
	 * @return
	 */
	public static <T> List<Class<T>> scan(String basePackage, Class<T> superType) {
		PackageScanUtil<T> s = new PackageScanUtil<T>(basePackage, superType);
		List<Class<T>> result = s.scanStart();
		return result;
	}

//	private PackageScanUtil(String basePackage, Class<?> superType, Class<?> annotationType, boolean allTrue) {
//		this.basePackage = basePackage;
//		this.superType = superType;
//		this.annotationType = annotationType;
//	}

	private PackageScanUtil(String basePackage, Class<?> superType) {
		this.basePackage = basePackage;
		this.superType = superType;
	}

	private List<Class<T>> scanStart() {
		URL url = classLoader.getResource(basePackage.replace(".", "/"));
		String protocol = url.getProtocol();
		if ("file".equals(protocol)) {
			findClassLocal(basePackage); // 本地自己可见的代码
		} else if ("jar".equals(protocol)) {
			findClassJar(basePackage); // 引用jar包的代码
		}
		return foundList;
	}

	/**
	 * 对比筛选
	 */
	@SuppressWarnings("unchecked")
	private void loadClassComparison(String clazzPath) {

		Class<?> clazz;
		try {
			clazz = classLoader.loadClass(clazzPath);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			return;
		}

		/* 判断继承 */
		boolean superFound = superType != null && superType.isAssignableFrom(clazz);
		if (superFound)
			foundList.add((Class<T>) clazz);

//		/* 判断注解 */
//		boolean annotationFound = false;
//		Annotation[] annotation = clazz.getAnnotations();
//		for (Annotation annotation2 : annotation) {
//			annotationFound |= annotation2.annotationType() == annotationType;
//			if (annotationFound) break;
//		}

//		if (allTrue && superFound && annotationFound) { // 必须都满足
//			foundList.add(clazz);
//		}
//		if (!allTrue && (superFound || annotationFound)) { // 任意满足一个即可
//			foundList.add(clazz);
//		}
	}

	/**
	 * 本地查找
	 * 
	 * @param packName
	 */
	private void findClassLocal(final String packName) {
		URI url = null;
		try {
			url = classLoader.getResource(packName.replace(".", "/")).toURI();
		} catch (URISyntaxException e1) {
			throw new RuntimeException("指定包不存在");
		}

		File file = new File(url);
		file.listFiles(new FileFilter() {
			public boolean accept(File chiFile) {
				if (chiFile.isDirectory()) {
					findClassLocal(packName + "." + chiFile.getName());
				}
				if (chiFile.getName().endsWith(".class")) {
					loadClassComparison(packName + "." + chiFile.getName().replace(".class", "")); // 判断条件
					return true;
				}
				return false;
			}
		});
	}

	/**
	 * jar包查找
	 * 
	 * @param packName
	 */
	private void findClassJar(final String packName) {
		String pathName = packName.replace(".", "/");
		JarFile jarFile = null;
		try {
			URL url = classLoader.getResource(pathName);
			JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
			jarFile = jarURLConnection.getJarFile();
		} catch (IOException e) {
			throw new RuntimeException("指定包不存在");
		}

		Enumeration<JarEntry> jarEntries = jarFile.entries();
		while (jarEntries.hasMoreElements()) {
			JarEntry jarEntry = jarEntries.nextElement();
			String jarEntryName = jarEntry.getName();

			if (jarEntryName.contains(pathName) && !jarEntryName.equals(pathName + "/")) {
				// 递归遍历子目录
				if (jarEntry.isDirectory()) {
					String clazzName = jarEntry.getName().replace("/", ".");
					int endIndex = clazzName.lastIndexOf(".");
					String prefix = null;
					if (endIndex > 0) {
						prefix = clazzName.substring(0, endIndex);
					}
					findClassJar(prefix);
				}

				if (jarEntry.getName().endsWith(".class")) {
					loadClassComparison(jarEntry.getName().replace("/", ".").replace(".class", "")); // 判断条件
				}
			}

		}

	}

}